<?php

!defined('IN_VIFRAME') && die('Access Denied.');

Vi::import('Exception : System.Db.Db');

class Db extends TLib {
	
	const
		ERR_RETRY         = '.Retry',
		ERR_SILENT        = '.Silent',
		ERR_HALT          = '.Halt';

	protected static
		$_Connection      = null,
		$_Result          = null,
		$_Count           = null,
		$_Array           = null;
		
	protected static
		$_TableNamePrefix = null,
		$_TableNames      = array();

	protected static
		$_QuerySqls       = array(),
		$_QueryCount      = 0;

	public static
		$_Version         = null;

	public static function connect($config = false) {
		if (!is_array($config)) {
			$config = Config::get('Db.DefaultConnection');
		}
		
		try {
			self::$_Connection = mysql_connect($config['Host'], $config['User'], $config['Password']);
		} catch(Exception $e) {
			throw new EDb('Connection');
		}

		mysql_select_db($config['Name']);
		mysql_query("SET NAMES {$config['Charset']}");
		mysql_set_charset($config['Charset']);
		self::$_TableNamePrefix = $config['Prefix'];
		
		return true;
	}

	public static function close() {
		return mysql_close(self::$_Connection);
	}
	
	public static function getTableName($key) {
		$name = isset(self::$_TableNames[$key]) ? self::$_TableNames[$key]
				: self::$_TableNamePrefix.$key;
		
		return $name;
	}
	
	public static function setTablePrefix($prefix) {
		self::$_TableNamePrefix = $prefix;
		
		return true;
	}
	
	public static function setTableName($key, $name) {
		if (is_array($key)) {
			self::$_TableNames = $key + self::$_TableNames;
		} else {
			self::$_TableNames[$key] = $name;
		}
		
		return true;
	}

	public static function select($key, $field, $condition = null, $config = array()) {
		$config = self::_filterConfigParam($config, array(
			'Limit'    => -1,
			'GetArray' => false,
			'Type'     => MYSQL_ASSOC, 
			'Error'    => 'Select'  
		));
		$table  = self::getTableName($key);
		if (is_array($field)) {
			$field = implode('`,`', $field);
		} else {
			$field = str_replace(',', '`,`', $field);
		}
		$field  = "`$field`";
		$where  = self::filterCondition($condition);
		$limit  = $config['Limit'] > 0 ? " LIMIT {$config['Limit']}" : '';
		$sql    = "SELECT $field FROM `$table`$where$limit";
		
		$result = self::query($sql, $config);
		if ($result && $config['GetArray']) {
			$result = self::getArray($config['Type']);
		}
		
		return $result;
	}
	
	public static function insert($key, $data, $config = array()) {
		$config  = self::_filterConfigParam($config, array(
			'GetInsertId' => false,
			'Replace'     => false,
			'Error'       => 'Insert'
		));
		$cmd     = $config['Replace'] ? 'REPLACE INTO' : 'INSERT INTO';
		$table   = self::getTableName($key);
		$dataSql = self::implodeData($data);
		$sql     = "$cmd `$table` SET $dataSql";
		
		$return  = self::query($sql, $config);
		return ($config['GetInsertId']) ? self::getInsertId() : $return;
	}
	
	public static function replace($key, $data, $config = array()) {
		$config['Replace'] = true;
		$config['Error']   = 'Replace';
		return self::insert($key, $data, $config);
	}
	
	public static function update($key, $data, $condition = null, $config = array()) {
		$config  = self::_filterConfigParam($config, array(
			'LowPriority' => false,
			'Error'       => 'Update'
		));
		$table   = self::getTableName($key);
		$dataSql = self::implodeData($data);
		$where   = self::filterCondition($condition);
		$sql     = 'UPDATE'.($config['LowPriority'] ? ' LOW_PRIORITY' : '')." `$table` SET $dataSql$where";
		
		return self::query($sql, $config); 
	}
	
	public static function delete($key, $condition = null, $config = array()) {
		$config = self::_filterConfigParam($config, array(
			'Limit' => -1,
			'Error' => 'Delete'
		));
		$table  = self::getTableName($key);
		$where  = self::filterCondition($condition);
		$limit  = $config['Limit'] > 0 ? " LIMIT {$config['Limit']}" : '';
		$sql    = "DELETE FROM `$table`$where$limit";
		
		return self::query($sql, $config);
	}
	
	public static function query($sql, $config = array()) {
		$config        = self::_filterConfigParam($config, array(
			'Unbuffered' => false,
			'OnError'    => VI_DB_ONERROR,
			'Error'      => 'Query'
		));
		$func          = ($config['Unbuffered'] && @function_exists('mysql_unbuffered_query'))
				? 'mysql_unbufferd_query' : 'mysql_query';
		
		self::$_Array  = null;
		self::$_Count  = -1;
		self::$_Result = $func($sql, self::$_Connection);
		
		if (!self::$_Result) {
			if (in_array(self::getErrno(), array(2006, 2013)) && $config['OnError'] = self::ERR_RETRY) {
				self::connect();
				return self::query($sql);
			}
			if ($config['OnError'] == self::ERR_HALT) {
				throw new EDb($config['Error']);
			}
		}
		
		self::$_QueryCount++;
		self::$_QuerySqls[] = $sql;
		
		return self::$_Result;
	}
	
	public static function fetch($type = MYSQL_ASSOC) {
		return mysql_fetch_array(self::$_Result, $type);
	}
	
	public static function getArray($type = MYSQL_ASSOC) {
		if (empty(self::$_Array)) {
			while ($row = mysql_fetch_array(self::$_Result, $type)) {
				self::$_Array[] = $row;
			}
		}
		
		return self::$_Array;
	}
	
	public static function getResult($row = 0) {
		return @mysql_result(self::$_Result, $row);
	}
	
	public static function getInsertId() {
		return mysql_insert_id(self::$_Connection);
	}
	
	public static function countAffectedRows() {
		return mysql_affected_rows(self::$_Connection);
	}
	
	public static function countRows() {
		return mysql_num_rows(self::$_Result);
	}
	
	public static function countFields() {
		return (self::$_Count >= 0 ? self::$_Count : self::$_Count = mysql_num_fields(self::$_Result));
	}
	
	public static function getError() {
		return mysql_error(self::$_Connection);
	}
	
	public static function getErrno() {
		return mysql_errno(self::$_Connection);
	}
	
	public static function freeResult() {
		return mysql_free_result(self::$_Result);
	}
	
	public static function getSqlVersion() {
		if(empty(self::$_Version)) {
			self::$_Version = mysql_get_server_info(self::$_Connection);
		}
		
		return self::$_Version;
	}

	public static function strToDate($data, $type = 'DateTime', $format = false) {
		$temp   = explode(' ', $data);
		$date   = array(1, 1, 1970);
		$time   = array(0, 0, 0);
		
		switch ($type) {
			case 'Date':
				$date   = explode('-',$temp[0]);
				break;
			case 'Time':
				$time   = explode(':',$temp[0]);
				break;
			default:
				$date   = explode('-',$temp[0]);
				$time   = explode(':',$temp[1]);
		}
		
		$result = mktime($time[0],$time[1],$time[2],$date[1],$date[2],$date[0]);
		return ($format === false ? $result : date($format,$result));
	}
	
	public static function filterCondition($condition) {
		$where = '';
		if (!empty($condition)) {
			$where = '';
			if (is_array($condition)) {
				$condition = self::implodeData($condition, ' AND ');
			}
			$where = " WHERE $condition";
		}
		
		return $where;
	}
	
	public static function implodeData($data, $split = ' , ') {
		$sql = $cntSplit = '';
		
		foreach ($data as $key => $value) {
			$sql     .= "$cntSplit`$key` = '$value'";
			$cntSplit = $split ;
		}
		
		return $sql;
	}
}
